package com.aispeech.task.mgr.fsm;

import com.aispeech.task.lasr.event.EventType;
import com.aispeech.task.mgr.event.TaskEvent;
import lombok.Getter;
import org.apache.flink.api.java.tuple.Tuple2;

import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class State implements Serializable {
	protected static final Map<String, State> nameEnumMap = new ConcurrentHashMap<>();

	@Getter
	protected String name;

	protected final Transition[] transitions;

	public State(Transition... transitions) {
		this.transitions = transitions;
	}

	public State(String name, Transition... transitions) {
		this.name = name;
		if (!nameEnumMap.containsKey(name)) {
			nameEnumMap.put(name, this);
		}

		this.transitions = transitions;
	}

	public Tuple2<State, TaskEvent> transition(TaskEvent event) {
		TaskEvent next = event.fire();
		return new Tuple2<>(transition(next.eventType()), next);
	}

	public State transition(EventType type) {
		for (Transition t : transitions) {
			if (t.type() == type) {
				return t.target();
			}
		}

		// no transition found
		return Invalid;
	}

	public boolean isTerminal() {
		return transitions.length == 0;
	}

	public boolean equals(State state) {
		return name == null || state == null ? false : name.equals(state.name);
	}

	public String toString() { return name; }

	public static State valueOf(String name) {
		if (name == null)
			throw new NullPointerException("name is null");

		State state = nameEnumMap.get(name);

		if (state != null) {
			return state;
		} else {
			throw new IllegalArgumentException("No enum constant exists, name is : " + name);
		}
	}

	public static final State Terminal = new State("Terminal");
	public static final State Invalid = new State("Invalid");
	public static final State Retry = new State("Retry");
}
